package gorm

import (
	"bytes"
	"fmt"
	"reflect"
	"strings"
)

// SqlBuilder SQL构建器
type SqlBuilder struct {
	buf  bytes.Buffer
	vars []any
	db   *DB
}

// SqlToken SQL token构建器
type SqlToken struct {
	content string
	vars    []any
}

func (s *SqlToken) Token() (string, []any) {
	return s.content, s.vars
}

func NewBuilder() *SqlBuilder {
	return new(SqlBuilder)
}

// Select Select部分
func (s *SqlBuilder) Select(fields ...*SqlToken) *SqlBuilder {
	if len(fields) <= 0 {
		s.buf.WriteString(fmt.Sprintf("select * "))
	} else {
		s.buf.WriteString(fmt.Sprintf("select "))
		for i, field := range fields {
			s.buf.WriteString(fmt.Sprintf("%s", field.content))
			if i != len(fields)-1 {
				s.buf.WriteString(", ")
			}
		}
	}
	return s
}

func (s *SqlBuilder) Update() *SqlBuilder {
	s.buf.WriteString("update ")
	return s
}

func (s *SqlBuilder) Delete() *SqlBuilder {
	s.buf.WriteString("delete ")
	return s
}

func (s *SqlBuilder) Set(fields ...*SqlToken) *SqlBuilder {
	if len(fields) <= 0 {
		return s
	}
	s.buf.WriteString("set ")
	for i, v := range fields {
		s.vars = append(s.vars, v.vars...)
		s.buf.WriteString(v.content)
		if i != len(fields)-1 {
			s.buf.WriteString(", ")
		}
	}
	return s
}

// From 查询的表
func (s *SqlBuilder) From(tableName string, alias ...string) *SqlBuilder {
	if len(alias) > 0 {
		s.buf.WriteString(fmt.Sprintf(" from %s %s", tableName, alias[0]))
	} else {
		s.buf.WriteString(fmt.Sprintf(" from %s", tableName))
	}
	return s
}

func (s *SqlBuilder) Table(tableName string, alias ...string) *SqlBuilder {
	if len(alias) > 0 {
		s.buf.WriteString(fmt.Sprintf("%s %s ", tableName, alias[0]))
	} else {
		s.buf.WriteString(fmt.Sprintf("%s ", tableName))
	}
	return s
}

// FromQuery 子查询构建
func (s *SqlBuilder) FromQuery(handler func(query *SqlBuilder), alias string) *SqlBuilder {
	s.buf.WriteString(" ( ")
	handler(s)
	s.buf.WriteString(fmt.Sprintf(") %s ", alias))
	return s
}

func (s *SqlBuilder) IJoin(tableName, on string) *SqlBuilder {
	s.buf.WriteString(fmt.Sprintf("inner join %s on %s ", tableName, on))
	return s
}

func (s *SqlBuilder) LJoin(tableName, on string) *SqlBuilder {
	s.buf.WriteString(fmt.Sprintf("left join %s on %s ", tableName, on))
	return s
}

func (s *SqlBuilder) RJoin(tableName, on string) *SqlBuilder {
	s.buf.WriteString(fmt.Sprintf("right join %s on %s ", tableName, on))
	return s
}

func (s *SqlBuilder) Union(query func(builder *SqlBuilder)) *SqlBuilder {
	s.buf.WriteString("union ")
	query(s)
	s.buf.WriteByte(' ')
	return s
}

func (s *SqlBuilder) UnionAll(query func(builder *SqlBuilder)) *SqlBuilder {
	s.buf.WriteString("union all ")
	query(s)
	s.buf.WriteByte(' ')
	return s
}

// Where 条件构建
func (s *SqlBuilder) Where(query ...*SqlToken) *SqlBuilder {
	if len(query) <= 0 {
		return s
	}
	s.buf.WriteString(" where ")
	for i, v := range query {
		s.vars = append(s.vars, v.vars...)
		s.buf.WriteString(v.content)
		if i != len(query)-1 {
			s.buf.WriteString(" and ")
		}
	}
	return s
}

// WhereOr 条件构建
func (s *SqlBuilder) WhereOr(query ...*SqlToken) *SqlBuilder {
	if len(query) <= 0 {
		return s
	}
	s.buf.WriteString(" where ")
	for i, v := range query {
		s.vars = append(s.vars, v.vars...)
		s.buf.WriteString(v.content)
		if i != len(query)-1 {
			s.buf.WriteString(" or ")
		}
	}
	return s
}

func (s *SqlBuilder) GroupBy(fieldName string) *SqlBuilder {
	s.buf.WriteString(fmt.Sprintf(" group by %s", fieldName))
	return s
}

func (s *SqlBuilder) OrderBy(query ...*SqlToken) *SqlBuilder {
	if len(query) <= 0 {
		return s
	}
	s.buf.WriteString(" order by ")
	for i, v := range query {
		s.buf.WriteString(v.content)
		if i != len(query)-1 {
			s.buf.WriteString(", ")
		}
	}
	return s
}

func (s *SqlBuilder) Limit(limit int64) *SqlBuilder {
	s.buf.WriteString(fmt.Sprintf(" limit %d ", limit))
	return s
}

func (s *SqlBuilder) Offset(offset int64) *SqlBuilder {
	s.buf.WriteString(fmt.Sprintf(" offset %d ", offset))
	return s
}

func (s *SqlBuilder) ToSql() (*DB, string, []any) {
	return s.db, s.buf.String(), s.vars
}

func (s *SqlBuilder) QExecute(value any) (tx *DB) {
	tx = s.db.getInstance()
	tx.Statement.SQL.WriteString(s.buf.String())
	tx.Statement.Vars = s.vars
	tx.Statement.Dest = value
	return tx.callbacks.TQuery().Execute(tx)
}

func (s *SqlBuilder) DExecute() (tx *DB) {
	tx = s.db.getInstance()
	tx.Statement.SQL.WriteString(s.buf.String())
	tx.Statement.Vars = s.vars
	return tx.callbacks.TDelete().Execute(tx)
}

func (s *SqlBuilder) UExecute() (tx *DB) {
	tx = s.db.getInstance()
	tx.Statement.SQL.WriteString(s.buf.String())
	tx.Statement.Vars = s.vars
	return tx.callbacks.TUpdate().Execute(tx)
}

func SetField(fieldName string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s=?", fieldName),
		vars:    []any{value},
	}
}

func Field(name string, alias ...string) *SqlToken {
	var filed string
	if len(alias) > 0 {
		filed = fmt.Sprintf("%s.%s", alias[0], name)
	}
	filed = fmt.Sprintf("%s", name)
	return &SqlToken{
		content: filed,
		vars:    nil,
	}
}

func Count(name string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("count(%s)", name),
		vars:    nil,
	}
}

func Max(name string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("max(%s)", name),
		vars:    nil,
	}
}

func Min(name string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("min(%s)", name),
		vars:    nil,
	}
}

func Eq(field string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s = ?", field),
		vars:    []any{value},
	}
}
func Nq(field string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s != ?", field),
		vars:    []any{value},
	}
}
func Gt(field string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s > ?", field),
		vars:    []any{value},
	}
}
func Gte(field string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s >= ?", field),
		vars:    []any{value},
	}
}
func Lt(field string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s < ?", field),
		vars:    []any{value},
	}
}
func Lte(field string, value any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s <= ?", field),
		vars:    []any{value},
	}
}

// Between Between查询
func Between(field string, value1, value2 any) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s between ? and ?", field),
		vars:    []any{value1, value2},
	}
}

// In In查询
func In(field string, values ...any) *SqlToken {
	t := make([]string, 0, len(values))
	v := make([]any, 0, len(values))
	for i := 0; i < len(values); i++ {
		value := reflect.ValueOf(values[i])
		kind := value.Kind()
		if kind == reflect.Slice || kind == reflect.Array {
			for j := 0; j < value.Len(); j++ {
				v = append(v, value.Index(j).Interface())
				t = append(t, "?")
			}
		} else {
			v = append(v, values[i])
			t = append(t, "?")
		}
	}
	return &SqlToken{
		content: field + " in (" + strings.Join(t, ",") + ")",
		vars:    v,
	}
}

// NotIn 非In查询
func NotIn(field string, values ...any) *SqlToken {
	t := make([]string, 0, len(values))
	v := make([]any, 0, len(values))
	for i := 0; i < len(values); i++ {
		value := reflect.ValueOf(values[i])
		kind := value.Kind()
		if kind == reflect.Slice || kind == reflect.Array {
			for j := 0; j < value.Len(); j++ {
				v = append(v, value.Index(j).Interface())
				t = append(t, "?")
			}
		} else {
			v = append(v, values[i])
			t = append(t, "?")
		}
	}
	return &SqlToken{
		content: field + " not in (" + strings.Join(t, ",") + ")",
		vars:    v,
	}
}

// And 与
func And(query ...SqlToken) *SqlToken {
	var (
		t = make([]string, 0)
		v = make([]any, 0)
	)
	for _, token := range query {
		t1, v1 := token.Token()
		t = append(t, t1)
		v = append(v, v1)
	}
	return &SqlToken{
		content: "(" + strings.Join(t, " and ") + ")",
		vars:    v,
	}
}

// Or 或
func Or(query ...*SqlToken) *SqlToken {
	var (
		t = make([]string, 0)
		v = make([]any, 0)
	)
	for _, token := range query {
		t1, v1 := token.Token()
		t = append(t, t1)
		if v1 != nil && len(v1) > 0 {
			v = append(v, v1...)
		}
	}
	return &SqlToken{
		content: "(" + strings.Join(t, " or ") + ")",
		vars:    v,
	}
}

// Like 左右模糊查询
func Like(field string, value string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s like concat('%%?%%')", field),
		vars:    []any{value},
	}
}

// LLike 左模糊查询
func LLike(field string, value string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s like concat('%%?')", field),
		vars:    []any{value},
	}
}

// RLike 右模糊查询
func RLike(field string, value string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s like concat('?%%')", field),
		vars:    []any{value},
	}
}

// Null 空值查询
func Null(field string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s is null", field),
		vars:    nil,
	}
}

// NotNull 非空值查询
func NotNull(field string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s is not null", field),
		vars:    nil,
	}
}

// Desc 降序
func Desc(field string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s desc", field),
		vars:    nil,
	}
}

// Asc 升序
func Asc(field string) *SqlToken {
	return &SqlToken{
		content: fmt.Sprintf("%s asc", field),
		vars:    nil,
	}
}
